package com.stellar.auth.domain.authUser.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.stellar.auth.basic.entity.AuthRole;
import com.stellar.auth.basic.entity.AuthUserRole;
import com.stellar.auth.basic.po.AuthUserPO;
import com.stellar.auth.basic.service.AuthRoleService;
import com.stellar.auth.basic.service.AuthUserRoleService;
import com.stellar.auth.config.DefaultUserProfileConfig;
import com.stellar.auth.constants.ErrorCodeConstants;
import com.stellar.auth.controller.admin.authRole.vo.AuthRoleResponseVO;
import com.stellar.auth.controller.miniApp.authUser.vo.MiniAppLoginResponseVO;
import com.stellar.auth.domain.authRole.AuthRoleDomainService;
import com.stellar.auth.enums.UserStatusEnum;
import com.stellar.auth.basic.entity.AuthUser;
import com.stellar.auth.basic.service.AuthUserService;
import com.stellar.auth.controller.admin.authUser.vo.AuthUserLoginResponseVO;
import com.stellar.auth.controller.admin.authUser.vo.AuthUserRequestPageVO;
import com.stellar.auth.controller.admin.authUser.vo.AuthUserRequestVO;
import com.stellar.auth.controller.admin.authUser.vo.AuthUserResponseVO;
import com.stellar.auth.convert.AuthUserConvert;
import com.stellar.auth.domain.authUser.AuthUserDomainService;

import com.stellar.redis.core.constants.RedisKeyConstants;
import com.stellar.config.properties.JwtProperties;
import com.stellar.core.constant.JwtClaimsConstant;
import com.stellar.core.context.LoginContextHolder;
import com.stellar.core.util.JwtUtil;
import com.stellar.core.util.NumericIdGenerator;
import com.stellar.redis.core.util.RedisUtil;
import com.stellar.web.core.bean.PageResult;
import com.stellar.web.core.constants.AuthUserConstants;
import com.stellar.web.core.enums.IsDeletedFlagEnum;

import com.stellar.web.core.enums.IsEnabledFlagENEnum;
import com.stellar.web.core.exception.PermissionException;
import com.stellar.wechat.core.entity.WxSessionInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.stellar.auth.constants.ErrorCodeConstants.AUTH_MINI_APP_LOGIN_FAIL;
import static com.stellar.core.util.ServiceExceptionUtil.exception;

/**
 * 系统用户信息表领域服务实现
 *
 * @author Jqchan
 * @version 1.0
 * @date 2025-05-16 18:05:35
 */
@Service
@Slf4j
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;

    @Resource
    private AuthRoleDomainService authRoleDomainService;

    @Resource
    private AuthUserRoleService authUserRoleService;

    @Resource
    private JwtProperties jwtProperties;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private AuthRoleService authRoleService;

    @Override
    public PageResult<AuthUserResponseVO> page(AuthUserRequestPageVO requestPageVO) {
        PageResult<AuthUserResponseVO> pageResult = new PageResult<>();
        AuthUser queryEntity = AuthUserConvert.INSTANCE.convert(requestPageVO);
        queryEntity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());

        List<AuthUser> entities = authUserService.page(queryEntity,
                requestPageVO.getStart(), requestPageVO.getPageSize());

        if (entities.isEmpty()) {
            return pageResult;
        }

        Long count = authUserService.count(queryEntity);
        List<AuthUserResponseVO> vos = AuthUserConvert.INSTANCE.convertEntityToRespList(entities);

        pageResult.setResult(vos);
        pageResult.setTotal(count.intValue());
        return pageResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(AuthUserRequestVO requestVO) {
        AuthUser entity = AuthUserConvert.INSTANCE.convert(requestVO);
        if (ObjectUtil.isNotNull(entity) && ObjectUtil.isNotNull(requestVO.getRoleId())) {
            AuthUserRole queryEntity = new AuthUserRole();
            queryEntity.setUserId(entity.getId());
            queryEntity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
            AuthUserRole authUserRole = authUserRoleService.queryByCondition(queryEntity);
            if (ObjectUtil.isNotNull(authUserRole)) {
                authUserRole.setRoleId(requestVO.getRoleId());
                authUserRoleService.update(authUserRole);
            }
        }
        this.clearRedisUserInfoCache(entity.getId());
        return authUserService.update(entity) != 0;
    }

    @Override
    public Boolean delete(Long id) {
        AuthUser entity = new AuthUser();
        entity.setId(id);
        entity.setIsDeleted(IsDeletedFlagEnum.DELETED.getVal());
        this.clearRedisUserInfoCache(id);
        return authUserService.update(entity) != 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(AuthUserRequestVO requestVO) {
        // 插入用户信息
        AuthUser entity = AuthUserConvert.INSTANCE.convert(requestVO);
        AuthUser result = authUserService.insert(entity);

        // 如果用户插入成功，处理角色关联
        if (ObjectUtil.isNotNull(result)) {
            Long userId = entity.getId();
            Long roleId = requestVO.getRoleId();

            // 如果指定了角色ID，则直接关联指定角色
            if (ObjectUtil.isNotNull(roleId)) {
                associateUserRole(userId, roleId);
            } else {
                // 如果未指定角色ID，则关联默认的普通用户角色
                associateDefaultRole(userId);
            }
        }
        return result.getId();
    }

    /**
     * 关联用户和角色
     *
     * @param userId 用户ID
     * @param roleId 角色ID
     */
    private void associateUserRole(Long userId, Long roleId) {
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(userId);
        authUserRole.setRoleId(roleId);
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
        authUserRoleService.insert(authUserRole);
    }

    /**
     * 关联默认的普通用户角色
     *
     * @param userId 用户ID
     */
    private void associateDefaultRole(Long userId) {
        // 查询默认的普通用户角色
        AuthRole queryEntity = new AuthRole();
        queryEntity.setRoleKey(AuthUserConstants.NORMAL_USER);
        queryEntity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
        queryEntity.setRoleStatus(IsEnabledFlagENEnum.ENABLED.getVal());
        AuthRole authRole = authRoleService.queryByCondition(queryEntity);

        // 如果查询到默认角色，则进行关联
        if (authRole != null) {
            associateUserRole(userId, authRole.getId());
        } else {
            // 未查询到默认角色
            throw exception(ErrorCodeConstants.AUTH_USER_ROLE_RELEVANCY_FAIL);
        }
    }

    @Override
    public AuthUserResponseVO get(Long id) {
        // 从缓存中获取用户信息
        AuthUserResponseVO redisCache = this.getFromRedisCache(id);
        if (ObjectUtil.isNotNull(redisCache)) {
            return redisCache;
        }
        AuthUser entity = authUserService.queryById(id);
        AuthUserResponseVO authUserResponseVO = AuthUserConvert.INSTANCE.convert(entity);
        if (ObjectUtil.isNotNull(authUserResponseVO)) {
            List<AuthRoleResponseVO> authRoleResponseVO = authRoleDomainService.getRoleByUserId(id);
            String roleKeys = buildAuthRoleKeys(authRoleResponseVO);
            authUserResponseVO.setRoleKeys(convertRoleKeysToList(roleKeys));
            this.cacheUserInfo(entity, roleKeys, null);
        }
        return authUserResponseVO;
    }

    /**
     * 构建角色标识 - 使用 逗号 拼接
     *
     * @param authRoleResponseVO
     * @return
     */
    private String buildAuthRoleKeys(List<AuthRoleResponseVO> authRoleResponseVO) {
        if (CollectionUtil.isEmpty(authRoleResponseVO)) {
            return null;
        }
        List<String> roleKeys = authRoleResponseVO.stream().map(AuthRoleResponseVO::getRoleKey).collect(Collectors.toList());
        return String.join(",", roleKeys);
    }

    /**
     * TODO 待优化：使用手机验证码或者邮箱验证码登陆
     *
     * @param authUserRequestVO
     * @return
     */
    @Override
    public AuthUserLoginResponseVO doLogin(AuthUserRequestVO authUserRequestVO) {
        AuthUser authUserConvert = AuthUserConvert.INSTANCE.convert(authUserRequestVO);
        // 验证用户信息
        AuthUser authUser = validateUser(authUserConvert);
        // 获取角色身份
        List<AuthRoleResponseVO> authRoleResponseVOS = authRoleDomainService.getRoleByUserId(authUser.getId());

        // 如果是 NORMAL_USER - 普通用户 不允许登录
        if (CollectionUtil.isNotEmpty(authRoleResponseVOS) &&
                authRoleResponseVOS.stream().anyMatch(authRoleResponseVO -> AuthUserConstants.NORMAL_USER.equals(authRoleResponseVO.getRoleKey()))) {
            throw exception(ErrorCodeConstants.AUTH_USER_NOT_EXIST);
        }

        String roleKeys = buildAuthRoleKeys(authRoleResponseVOS);

        // 生成token
        HashMap<String, Object> clalms = new HashMap<>();
        clalms.put(JwtClaimsConstant.ADMIN_USER_ID, authUser.getId());
        clalms.put(JwtClaimsConstant.COMMON_USER_ROLE, roleKeys);

        String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), clalms);

        // 缓存用户信息
        this.cacheUserInfo(authUser, roleKeys, token);

        // 更新用户操作信息
        authUser.setLastIp(LoginContextHolder.getIp());
        authUser.setLastLoginTime(DateUtil.date());
        authUser.setLoginCount(authUser.getLoginCount());
        authUserService.update(authUser);


        return AuthUserLoginResponseVO.builder()
                .id(authUser.getId())
                .avatar(authUser.getAvatar())
                .token(token)
                .nickName(authUser.getNickName())
                .roleKeys(convertRoleKeysToList(roleKeys))
                .build();
    }

    /**
     * 角色标识转换成List
     *
     * @param roleKeys
     * @return
     */
    private List<String> convertRoleKeysToList(String roleKeys) {
        if (StringUtils.isBlank(roleKeys)) {
            return null;
        }
        return Arrays.asList(roleKeys.split(","));
    }

    @Override
    public AuthUserResponseVO getUserInfo() {
        String userId = LoginContextHolder.getLoginId();
        if (StringUtils.isBlank(userId)) {
            throw exception(ErrorCodeConstants.AUTH_USER_NOT_LOGIN);
        }
        return this.get(Long.valueOf(userId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MiniAppLoginResponseVO miniAppLogin(WxSessionInfo wxSessionInfo) {
        try {
            // 参数校验
            this.validateWxSessionInfo(wxSessionInfo);

            // 处理微信用户信息
            return this.handlerWxSessionInfo(wxSessionInfo);
        } catch (Exception e) {
            log.error("微信小程序登录失败，openid: {}", wxSessionInfo.getOpenid(), e);
            throw exception(AUTH_MINI_APP_LOGIN_FAIL);
        }
    }

    @Override
    public Boolean identification(AuthUserRequestVO authUserRequestVO) {
        AuthUser authUser = new AuthUser();
        authUser.setId(authUser.getId());
        authUser.setSchoolId(authUserRequestVO.getSchoolId());
        authUser.setGrade(authUserRequestVO.getGrade());
        return authUserService.update(authUser) != 0;
    }

    @Override
    public Boolean toggleStatus(Long id) {
        if (id == null || id <= 0) {
            throw exception(ErrorCodeConstants.AUTH_USER_NOT_EXIST);
        }

        AuthUser authUser = authUserService.queryById(id);
        if (authUser == null) {
            throw exception(ErrorCodeConstants.AUTH_USER_NOT_EXIST);
        }


        // 状态切换逻辑简化
        String currentStatus = authUser.getUserStatus();
        String newStatus;

        if (AuthUserConstants.USER_STATUS_NORMAL.equals(currentStatus)) {
            newStatus = AuthUserConstants.USER_STATUS_BANNED;
        } else if (AuthUserConstants.USER_STATUS_BANNED.equals(currentStatus)) {
            newStatus = AuthUserConstants.USER_STATUS_NORMAL;
        } else {
            throw exception(ErrorCodeConstants.AUTH_USER_STATUS_INVALID);
        }

        // 避免不必要的对象更新（仅当状态变化时更新）
        if (!authUser.getUserStatus().equals(newStatus)) {
            authUser.setUserStatus(newStatus);
            authUserService.update(authUser);
            return true;
        }

        return false;
    }

    @Override
    public AuthUserResponseVO getByPhone(String phone) {
        AuthUser queryEntity = new AuthUser();
        queryEntity.setPhone(phone);
        queryEntity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
        AuthUser authUser = authUserService.queryByCondition(queryEntity);
        if (ObjectUtil.isNull(authUser)) {
            return null;
        }
        return AuthUserConvert.INSTANCE.convert(authUser);
    }

    @Override
    public List<AuthUserResponseVO> batchQueryByIds(List<Long> ids) {
        List<AuthUser> authUsers = authUserService.batchQueryByIds(ids);
        if (CollectionUtil.isNotEmpty(authUsers)) {
            return AuthUserConvert.INSTANCE.convertEntityToRespList(authUsers);
        }
        return Collections.emptyList();
    }

    @Override
    public Long countBySchoolId(Long id) {
        AuthUser authUser = new AuthUser();
        authUser.setSchoolId(id);
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
        authUser.setUserStatus(AuthUserConstants.USER_STATUS_NORMAL);
        return authUserService.count(authUser);
    }


    /**
     * 处理微信用户信息 - 校验当前用户是否注册
     * 是：直接返回用户相关信息
     * 否：注册新用户
     *
     * @param wxSessionInfo
     */
    private MiniAppLoginResponseVO handlerWxSessionInfo(WxSessionInfo wxSessionInfo) {
        AuthUser authUser = authUserService.queryByOpenId(wxSessionInfo.getOpenid());
        MiniAppLoginResponseVO miniAppLoginResponseVO = null;
        // 校验用户帐号状态
        if (ObjectUtil.isNotNull(authUser)) {
            if (AuthUserConstants.USER_STATUS_BANNED.equals(authUser.getUserStatus())) {
                throw new PermissionException("帐号已被封禁，请联系管理员");
            }
            miniAppLoginResponseVO = buildMiniAppLoginResponseVO(authUser.getId(), wxSessionInfo.getOpenid(), getRoleKey(authUser.getId()), authUser.getSchoolId());
            // 更新用户操作信息
            authUser.setLastLoginTime(DateUtil.date());
            authUser.setLoginCount(authUser.getLoginCount() + 1);
            authUser.setLastIp(LoginContextHolder.getIp());
            authUserService.update(authUser);
        } else {
            // 注册新用户
            Long userId = register(wxSessionInfo.getOpenid());
            miniAppLoginResponseVO = buildMiniAppLoginResponseVO(userId, wxSessionInfo.getOpenid(), null,null);
        }
        return miniAppLoginResponseVO;
    }

    /**
     * 获取角色标识
     *
     * @param id 用户Id
     * @return
     */
    private String getRoleKey(Long id) {
        AuthUserResponseVO redisCache = this.getFromRedisCache(id);
        if (ObjectUtil.isNotNull(redisCache)) {
            List<String> roleKeys = redisCache.getRoleKeys();
            return String.join(",", roleKeys);
        } else {
            List<AuthUserRole> authUserRoles = authUserRoleService.queryByUserId(id);
            if (CollectionUtil.isNotEmpty(authUserRoles)) {
                StringBuilder sb = new StringBuilder();
                authUserRoles.forEach(a -> {
                    AuthRole authRole = authRoleService.queryById(a.getRoleId());
                    if (ObjectUtil.isNotNull(authRole)) {
                        sb.append(authRole.getRoleKey()).append(",");
                    }
                });
                return sb.toString();
            }
        }
        return null;
    }

    /**
     * 构建小程序登录响应对象
     *
     * @param userId   用户ID
     * @param openid   openID
     * @param roleKey  角色标识
     * @param schoolId 学校Id
     * @return
     */
    private MiniAppLoginResponseVO buildMiniAppLoginResponseVO(Long userId, String openid, String roleKey, Long schoolId) {
        String token = this.generateJwtToken(userId, openid, roleKey);
        return MiniAppLoginResponseVO.builder()
                .openid(openid)
                .userId(userId)
                .token(token)
                .schoolId(schoolId)
                .build();
    }

    /**
     * 生成JWT令牌
     *
     * @param userId  系统用户Id
     * @param openid  openid
     * @param roleKey 角色标识
     * @return
     */
    private String generateJwtToken(Long userId, String openid, String roleKey) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.MINI_APP_USER_ID, userId);
        claims.put(JwtClaimsConstant.MINI_APP_USER_OPEN_ID, openid);
        claims.put(JwtClaimsConstant.COMMON_USER_ROLE, roleKey != null ? roleKey : AuthUserConstants.NORMAL_USER);

        return JwtUtil.createJWT(
                jwtProperties.getMiniAppSecretKey(),
                jwtProperties.getMiniAppTtl(),
                claims
        );
    }


    /**
     * 初始化用户信息并注释
     *
     * @param openid
     * @return userId
     */
    private Long register(String openid) {
        AuthUserRequestVO requestVO = initUserProfile(openid);
        Long userId = insert(requestVO);
        if (userId == null) {
            throw exception(ErrorCodeConstants.AUTH_USER_REGISTER_FAIL);
        }
        return userId;
    }

    /**
     * 初始化用户信息
     *
     * @param openid
     * @return
     */
    private AuthUserRequestVO initUserProfile(String openid) {
        AuthUserRequestVO authUser = new AuthUserRequestVO();
        authUser.setOpenid(openid);
        authUser.setUserType(AuthUserConstants.WECHAT_USER); // 默认使用微信用户 - 发帖子时如果选择匿名帖，则需变更为匿名用户
        authUser.setUserStatus(AuthUserConstants.USER_STATUS_NORMAL);
        authUser.setDefaultAvatar(DefaultUserProfileConfig.getRandomDefaultAnonymousAvatar());
        authUser.setAvatar(DefaultUserProfileConfig.getRandomDefaultAvatar());
        authUser.setDefaultNickName(DefaultUserProfileConfig.getRandomDefaultAnonymousNickname());
        authUser.setNickName(DefaultUserProfileConfig.getRandomDefaultNickname());
        authUser.setStellarMeetCode(NumericIdGenerator.generateId());
        authUser.setLastLoginTime(DateUtil.date());
        authUser.setLoginCount(1);
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
        return authUser;
    }


    /**
     * 缓存用户信息
     *
     * @param authUser 用户信息
     * @param roleKeys 角色标识
     * @param token    token - 做踢人下线等操作,当前逻辑待优化，应使用kv结构去操控token状态
     */
    private void cacheUserInfo(AuthUser authUser, String roleKeys, String token) {
        // 缓存用户信息
        String userInfoKey = redisUtil.buildKey(RedisKeyConstants.USER_INFO.getCachePrefix(), authUser.getId().toString());
        if (redisUtil.exist(userInfoKey)) {
            redisUtil.del(userInfoKey);
        }
        AuthUserPO authUserPO = AuthUserConvert.INSTANCE.convertEntityToPO(authUser);
        authUserPO.setRoleKeys(convertRoleKeysToList(roleKeys));
        redisUtil.setNx(userInfoKey, JSONUtil.toJsonStr(authUserPO), RedisKeyConstants.USER_INFO.getCacheTime(), RedisKeyConstants.USER_INFO.getCacheTimeUnit());
        // 缓存token
        if (StringUtils.isNotBlank(token)) {
            String tokenKey = redisUtil.buildKey(RedisKeyConstants.USER_TOKEN.getCachePrefix(), authUser.getId().toString());
            if (redisUtil.exist(tokenKey)) {
                redisUtil.del(tokenKey);
            }
            redisUtil.setNx(tokenKey, token, RedisKeyConstants.USER_TOKEN.getCacheTime(), RedisKeyConstants.USER_TOKEN.getCacheTimeUnit());
        }
    }

    /**
     * 从缓存中获取用户信息
     *
     * @param id 用户id
     * @return
     */
    public AuthUserResponseVO getFromRedisCache(Long id) {
        if (ObjectUtil.isNull(id)) return null;
        String userInfoKey = redisUtil.buildKey(RedisKeyConstants.USER_INFO.getCachePrefix(), id.toString());
        String userInfo = redisUtil.get(userInfoKey);
        if (StringUtils.isBlank(userInfo)) return null;
        AuthUserPO authUserPO = JSONUtil.toBean(userInfo, AuthUserPO.class);
        return AuthUserConvert.INSTANCE.convertPOToResponseVO(authUserPO);
    }

    /**
     * 清除用户信息redis缓存
     *
     * @param id 用户id
     */
    private void clearRedisUserInfoCache(Long id) {
        if (ObjectUtil.isNull(id)) return;
        String userInfoKey = redisUtil.buildKey(RedisKeyConstants.USER_INFO.getCachePrefix(), id.toString());
        String tokenKey = redisUtil.buildKey(RedisKeyConstants.USER_TOKEN.getCachePrefix(), id.toString());
        redisUtil.del(userInfoKey);
        redisUtil.del(tokenKey);
    }


    /**
     * 校验用户信息
     *
     * @param authUser
     */
    private AuthUser validateUser(AuthUser authUser) {
        AuthUser queryEntity = new AuthUser();
        queryEntity.setPhone(authUser.getPhone());
        queryEntity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getVal());
        AuthUser user = authUserService.queryByCondition(queryEntity);
        if (Objects.isNull(user)) {
            throw exception(ErrorCodeConstants.AUTH_USER_NOT_EXIST);
        }
        // 帐号状态校验
        if (!Objects.equals(user.getUserStatus(), UserStatusEnum.NORMAL.getCode())) {
            throw exception(ErrorCodeConstants.AUTH_USER_ACCOUNT_LOCKED);
        }
        return user;
    }

    /**
     * 校验微信会话信息
     *
     * @param wxSessionInfo
     */
    private void validateWxSessionInfo(WxSessionInfo wxSessionInfo) {
        if (wxSessionInfo == null || com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(wxSessionInfo.getOpenid())) {
            throw new IllegalArgumentException("微信会话信息不完整");
        }
    }
}
